package org.proteored.miapeapi.xml.pride.adapter;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import org.apache.log4j.Logger;
import org.proteored.miapeapi.cv.ControlVocabularyManager;
import org.proteored.miapeapi.exceptions.IllegalMiapeArgumentException;
import org.proteored.miapeapi.interfaces.Adapter;
import org.proteored.miapeapi.interfaces.ms.MiapeMSDocument;
import org.proteored.miapeapi.util.SpectraReader;
import org.proteored.miapeapi.util.SpectumAdapterFromMGFSpectrum;
import org.proteored.miapeapi.xml.pride.adapter.mzml.MzMLSpectrumIDManager;
import org.proteored.miapeapi.xml.pride.adapter.util.MSResultingDataUtil;
import org.proteored.miapeapi.xml.pride.autogenerated.ExperimentType.MzData.SpectrumList;
import org.proteored.miapeapi.xml.pride.autogenerated.ExperimentType.MzData.SpectrumList.Spectrum;
import org.proteored.miapeapi.xml.pride.autogenerated.ObjectFactory;
import org.proteored.miapeapi.xml.util.peaklistreader.TMsData;
import org.proteored.miapeapi.xml.util.peaklistreader.TSpectrum;

public class SpectrumListAdapter implements Adapter<SpectrumList> {
	private static Logger log = Logger.getLogger("log4j.logger.org.proteored");

	private final List<MiapeMSDocument> miapeMSs = new ArrayList<MiapeMSDocument>();
	private final ObjectFactory factory;
	private final ControlVocabularyManager cvManager;

	private final boolean addPeakList;

	// has map with keys=miape ms identifier and value=offset of spectras
	private static final HashMap<Integer, Integer> miapeIDMGFSpectraOffsets = new HashMap<Integer, Integer>();
	private static final MzMLSpectrumIDManager mzMLSpectrumIdentifierManager = new MzMLSpectrumIDManager();
	/**
	 * Key: miapeMSID+spectrumRef Value: spectrum id in the MGF
	 */
	private static HashMap<String, Integer> mgfTitlesMaps = new HashMap<String, Integer>();

	private static HashMap<BigInteger, Spectrum> spectrumMap = new HashMap<BigInteger, Spectrum>();
	private static HashMap<Double, Spectrum> spectrumByMoverZMap = new HashMap<Double, Spectrum>();

	/**
	 * 
	 * @param factory
	 * @param cvManager
	 * @param miapeMSs
	 * @param addPeakList
	 * @param mgfOrder
	 *            order in which the mgf (if the resulting data contains a MGF
	 *            file) should be sorted before to be readed.<br>
	 *            Possible values comes from TMsData constants class
	 * @param resultingDataFileRedingOrder
	 *            key=MiapeMS document; value=Order in which mgf should be
	 *            readed, or null if it is not a mgf file
	 */
	public SpectrumListAdapter(ObjectFactory factory,
			ControlVocabularyManager cvManager, List<MiapeMSDocument> miapeMSs,
			boolean addPeakList) {
		this.miapeMSs.addAll(miapeMSs);
		this.factory = factory;
		this.cvManager = cvManager;
		this.addPeakList = addPeakList;
		SpectrumListAdapter.mzMLSpectrumIdentifierManager.clear();
		SpectrumListAdapter.miapeIDMGFSpectraOffsets.clear();
	}

	/**
	 * Gets a hashmap containing for each MIAPE MS (the key is the identifier),
	 * the offset that is needed to apply to its spectra
	 * 
	 * @return
	 */
	public static HashMap<Integer, Integer> getMiapeIDSpectraOffsets() {
		return miapeIDMGFSpectraOffsets;
	}

	public static MzMLSpectrumIDManager getMzMLSpectrumIDManager() {
		return SpectrumListAdapter.mzMLSpectrumIdentifierManager;
	}

	public static HashMap<BigInteger, Spectrum> getSpectrumMap() {
		return SpectrumListAdapter.spectrumMap;
	}

	public static HashMap<Double, Spectrum> getSpectrumByMoverZMap() {
		return SpectrumListAdapter.spectrumByMoverZMap;
	}

	@Override
	public SpectrumList adapt() {
		SpectrumList spectrumList = factory
				.createExperimentTypeMzDataSpectrumList();
		if (miapeMSs != null) {

			int offset = 0;

			for (MiapeMSDocument miapeMS : miapeMSs) {

				if (addPeakList) {
					try {
						boolean hasMGF = MSResultingDataUtil.hasMGFFile(
								miapeMS, cvManager);
						boolean hasMzML = MSResultingDataUtil.hasMzMLFile(
								miapeMS, cvManager);
						final SpectraReader spectraReader = SpectraReader
								.getInstance(cvManager);
						if (hasMGF) {

							log.info("Parse spectra from MGF");
							TMsData tMsData = spectraReader.getSpectraFromMGFs(
									MSResultingDataUtil
											.getMGFResultingDataFromMIAPEMS(
													miapeMS, cvManager),
									SpectrumListAdapter.mgfTitlesMaps);
							final HashMap<Integer, TSpectrum> spectra = tMsData
									.getSpectra();
							log.info(spectra.size() + " spectra added");

							// sort the queries numbers before to add to the
							// spectrum list
							List<Integer> queryList = new ArrayList<Integer>();
							for (Integer queryNumber : spectra.keySet()) {
								queryList.add(queryNumber);
							}
							Collections.sort(queryList);
							for (Integer queryNumber : queryList) {
								TSpectrum tSpectrum = spectra.get(queryNumber);
								log.debug(queryNumber + " spectrum mass="
										+ tSpectrum.getPeptideMoverZ());
								Spectrum xmlSpectrum = null;
								try {
									xmlSpectrum = new SpectumAdapterFromMGFSpectrum(
											tMsData, queryNumber, offset,
											factory, cvManager).adapt();
								} catch (IllegalMiapeArgumentException e) {
									log.warn(e.getMessage());
									continue;
								}
								SpectrumListAdapter.spectrumMap
										.put(BigInteger.valueOf(Long
												.valueOf(String
														.valueOf(xmlSpectrum
																.getId()))),
												xmlSpectrum);
								SpectrumListAdapter.spectrumByMoverZMap.put(
										tSpectrum.getPeptideMoverZ(),
										xmlSpectrum);
								spectrumList.getSpectrum().add(xmlSpectrum);
							}
							miapeIDMGFSpectraOffsets.put(miapeMS.getId(),
									offset);
							// the offset is the maximum spectrum index to
							// assure to
							// not overlap indexes with others mgfs
							offset = offset + tMsData.getMaxSpectrumIndex();

						}
						if (hasMzML) {

							log.info("Parse spectra from mzML");
							int numAddedSpectra = 0;

							numAddedSpectra = spectraReader.addSpectraFromMzML(
									spectrumList, MSResultingDataUtil
											.getMzMLResultingDataFromMIAPEMS(
													miapeMS, cvManager),
									offset, mzMLSpectrumIdentifierManager,
									miapeMS.getId());
							log.info(numAddedSpectra + " spectra added");
							miapeIDMGFSpectraOffsets.put(miapeMS.getId(),
									offset);

							offset = offset + numAddedSpectra;

						}
						if (!hasMGF && !hasMzML) {
							log.warn("Mass spectra data file not supported. Only mzML and MGF files are supported");
						}
					} catch (IllegalMiapeArgumentException e) {
						log.warn(e.getMessage());
					}
				}

			}

		}

		if (spectrumList.getSpectrum().isEmpty())
			log.info("No spectra was readed");
		if (spectrumList.getSpectrum().isEmpty() && addPeakList)
			throw new IllegalMiapeArgumentException(
					"No spectra has been captured");

		spectrumList.setCount(spectrumList.getSpectrum().size());
		log.info(spectrumList.getCount() + " spectra readed from "
				+ miapeMSs.size() + " MIAPE MSs");
		return spectrumList;
		// not return null because it is a mandatory field
		// return null;

	}

	/**
	 * Gets the reference of the spectrum for a given peptide. If the peptide
	 * comes from a mzML file, the mzMLSpectrumIdentifierManager is used.<br>
	 * If the peptide comes from a MGF file, the miapeIDMGFSpectraOffsets is
	 * used.<br>
	 * This is called from the PeptideAdapter to get the spectrumReference for
	 * each peptide
	 * 
	 * @param identifiedPeptide
	 * @return
	 */
	public static BigInteger getPeptideSpectrumReference(
			Integer miapeMSReference, String spectrumRefOriginal) {
		try {
			String key = miapeMSReference + spectrumRefOriginal;
			try {
				Integer.valueOf(spectrumRefOriginal);
			} catch (NumberFormatException e) {
				// log.info("The spectrum ref is an string!");
				int offset = 0;
				if (SpectrumListAdapter.miapeIDMGFSpectraOffsets
						.containsKey(miapeMSReference)) {
					offset = SpectrumListAdapter.miapeIDMGFSpectraOffsets
							.get(miapeMSReference);
				}

				if (SpectrumListAdapter.mgfTitlesMaps
						.containsKey(spectrumRefOriginal)) {
					Long l = Long.valueOf(SpectrumListAdapter.mgfTitlesMaps
							.get(spectrumRefOriginal) + offset);
					return BigInteger.valueOf(l);
				} else {
					// try to avoid return carriages in the spectrumRef.
					// Sometimes found in XTandemFiles
					if (spectrumRefOriginal.contains("\n")) {
						String cutSpectrumRef = spectrumRefOriginal.substring(
								0, spectrumRefOriginal.indexOf("\n"));
						if (SpectrumListAdapter.mgfTitlesMaps
								.containsKey(cutSpectrumRef)) {
							Long l = Long
									.valueOf(SpectrumListAdapter.mgfTitlesMaps
											.get(cutSpectrumRef) + offset);
							return BigInteger.valueOf(l);
						}
					}
				}
			}

			if (SpectrumListAdapter.mzMLSpectrumIdentifierManager
					.getSpectrumIndex(key) != -1)
				return BigInteger
						.valueOf(SpectrumListAdapter.mzMLSpectrumIdentifierManager
								.getSpectrumIndex(key));
			// if the code os here is because the spectrum has not found in the
			// mzMLSpectrumIdentifierManager, so it doesnot come from a mzML
			// file.
			// So, it comes from a mgf file
			if (SpectrumListAdapter.miapeIDMGFSpectraOffsets
					.containsKey(miapeMSReference)) {
				int offset = SpectrumListAdapter.miapeIDMGFSpectraOffsets
						.get(miapeMSReference);

				final Long spectrumRef = Long.valueOf(spectrumRefOriginal);
				return BigInteger.valueOf(offset + spectrumRef);

			}
		} catch (Exception e) {

		}
		return BigInteger.valueOf(-1);
	}
}
